Skip to content

feat(observability): server-only Sentry capture, inert without a DSN#600

Merged
BigSimmo merged 5 commits into
mainfrom
claude/sentry-server-capture
Jul 13, 2026
Merged

feat(observability): server-only Sentry capture, inert without a DSN#600
BigSimmo merged 5 commits into
mainfrom
claude/sentry-server-capture

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds @sentry/node (MIT) behind an optional SENTRY_DSN env var — fully inert when unset: the SDK is never imported, no network egress, no behavior change. Not part of requireServerEnv, so a missing DSN never blocks boot.
  • Server-only by design: no @sentry/nextjs wizard, no next.config.ts wrapping, no CSP changes, nothing shipped to the client bundle.
  • Privacy scrubbing at init (clinical app): sendDefaultPii: false, beforeSend strips request payloads/headers, breadcrumbs disabled entirely. Events carry only the error plus small operational context (route, status, sanitized reason) — never query text or document content.

Capture points

  • onRequestError instrumentation hook (Next 16 stable API) for uncaught route/RSC/action errors, with query strings stripped from paths.
  • /api/answer + /api/answer/stream catch blocks, filtered to server-fault (≥500) errors; client aborts and expected sub-500 degradations (quota/timeout → source-only fallback) are skipped to avoid noise.
  • rag.ts generation-fallback catch reports answer_generation_fallback as a warning event — this silent source-only degradation never reaches route-level capture and was the exact signature of the GEN-C1 token-starvation incident that previously took weeks to spot via manual SLO correlation.
  • All capture flows through a no-op-when-disabled helper (src/lib/observability/error-capture.ts); routes never import the SDK directly and capture failures can never break a request.

Verification

  • npm run verify:cheap — green (unit suite incl. 10 new tests across tests/instrumentation.test.ts and tests/error-capture.test.ts: DSN-unset no-ops, scrubber behavior, ≥500 filtering, SDK-failure swallowing).
  • npm run build — green, client-bundle secret scan passed (confirms nothing Sentry-related reaches the browser).
  • Prettier clean.

Operator follow-up (not in this PR)

Create a Sentry project and set SENTRY_DSN in the Railway service env. The code merges fully dormant until then.

Clinical governance preflight

Touches the answer pipeline error paths only — no retrieval, ranking, source rendering, or answer-content changes. Degraded-response behavior is byte-identical; the only addition is telemetry on already-existing failure branches, gated behind an env var that is unset in production today.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added optional server-side Sentry support via SENTRY_DSN (documented in .env.example) with privacy scrubbing.
    • Improved request-level error reporting in the Node.js runtime.
    • Emits a server warning event when answer generation falls back.
  • Bug Fixes
    • Suppresses error reporting for client aborts and for expected/non-500 errors.
    • Ensures streaming and non-streaming paths report consistently.
  • Tests
    • Expanded Vitest coverage for Sentry gating, privacy behavior, and safe no-op/failure containment.

Add @sentry/node behind an optional SENTRY_DSN env var. Without a DSN the SDK
is never imported and nothing changes; with one, init happens last in the
instrumentation boot guard with privacy scrubbing (no request bodies, headers,
breadcrumbs, or PII — clinical query text must never leave the box).

Capture points:
- onRequestError instrumentation hook for uncaught route/RSC/action errors
  (path stripped of query strings)
- answer + answer/stream route catches, filtered to server-fault (>=500)
  errors only; client aborts and expected sub-500 degradations are skipped
- rag.ts generation-fallback catch reports answer_generation_fallback as a
  warning event — this silent source-only degradation is invisible to route
  captures and was the exact signature of the GEN-C1 token-starvation incident

All capture flows through a no-op-when-disabled helper
(src/lib/observability/error-capture.ts) so routes never import the SDK
directly and capture failures can never break a request.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 13, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: be6b2146-ce95-4fb2-815d-d9ad9e3fef2e

📥 Commits

Reviewing files that changed from the base of the PR and between 79fa894 and 1eb6a4b.

📒 Files selected for processing (4)
  • src/app/api/answer/stream/route.ts
  • src/lib/observability/error-capture.ts
  • tests/error-capture.test.ts
  • tests/instrumentation.test.ts
💤 Files with no reviewable changes (1)
  • tests/instrumentation.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/api/answer/stream/route.ts

📝 Walkthrough

Walkthrough

This change adds optional server-side Sentry integration, privacy-scrubbed instrumentation, lazy capture helpers, request error reporting, route-level failure capture, and RAG fallback events. Client aborts and non-server errors are excluded from exception reporting.

Changes

Server-side Sentry observability

Layer / File(s) Summary
Sentry configuration and capture helpers
.env.example, package.json, src/lib/env.ts, src/lib/observability/error-capture.ts, tests/error-capture.test.ts
Adds optional SENTRY_DSN configuration, the Sentry runtime dependency, lazy server-only capture helpers, and tests for disabled, enabled, redacted, and SDK-failure behavior.
Instrumentation initialization and request capture
src/instrumentation.ts, tests/instrumentation.test.ts
Initializes Sentry only when configured, disables tracing and default PII, scrubs request data and breadcrumbs, and captures sanitized Node.js request errors.
Route and RAG error reporting
src/app/api/answer/route.ts, src/app/api/answer/stream/route.ts, src/lib/rag.ts
Reports server failures from answer routes while excluding client aborts and non-5xx errors, and emits context for answer-generation fallback events.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AnswerRoute
  participant RAG
  participant ErrorCapture
  participant Sentry
  Client->>AnswerRoute: Submit answer request
  AnswerRoute->>RAG: Generate answer
  RAG->>ErrorCapture: Capture fallback event on degradation
  AnswerRoute->>ErrorCapture: Capture qualifying server exception
  ErrorCapture->>Sentry: Send exception or warning event
  AnswerRoute-->>Client: Return JSON response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: optional server-only Sentry capture that stays inert without a DSN.
Description check ✅ Passed The description covers the main summary, capture points, verification results, and clinical/privacy notes; only the exact checklist formatting is missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/sentry-server-capture

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo enabled auto-merge (squash) July 13, 2026 14:57

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c131b9b7ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/env.ts Outdated
Comment thread src/app/api/answer/stream/route.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/answer/route.ts`:
- Around line 197-204: Honor client aborts across all Sentry capture paths: in
src/app/api/answer/route.ts lines 197-204, compute clientAborted before the
PublicApiError branch and skip captureServerException for aborted requests; in
src/app/api/answer/stream/route.ts lines 102-106, pass the request signal to
logStreamError and guard capture; in src/app/api/answer/stream/route.ts lines
310-320, apply the same client-abort guard to outer catch captures.
- Line 200: Sanitize all exception payloads before calling
captureServerException: update the PublicApiError, generic, and unknown error
paths in src/app/api/answer/route.ts (lines 200, 214-215, and 221-223), and the
corresponding stream error paths in src/app/api/answer/stream/route.ts (lines
106, 311-312, and 315-319). Reuse the established error-normalization/scrubbing
mechanism so no raw clinical error payload is forwarded to Sentry.

In `@src/lib/observability/error-capture.ts`:
- Around line 9-12: Update captureServerException() to sanitize or wrap the
incoming error before passing it to captureException(...), removing potentially
sensitive exception messages and stack-frame content while preserving safe
operational context. Keep the existing privacy boundary and beforeSend behavior
unchanged.

In `@src/lib/rag.ts`:
- Around line 5175-5182: Ensure the answer_generation_fallback telemetry
initiated in the fallback branch is not dropped before capture completes: await
captureServerEvent before returning, or schedule it through the existing after()
mechanism. Preserve the sanitizedReason, queryClass, and routeMode payload.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5895c7f6-f43e-43a2-8828-40642202b17e

📥 Commits

Reviewing files that changed from the base of the PR and between e8330b2 and c131b9b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • .env.example
  • package.json
  • src/app/api/answer/route.ts
  • src/app/api/answer/stream/route.ts
  • src/instrumentation.ts
  • src/lib/env.ts
  • src/lib/observability/error-capture.ts
  • src/lib/rag.ts
  • tests/error-capture.test.ts
  • tests/instrumentation.test.ts

Comment thread src/app/api/answer/route.ts Outdated
Comment thread src/app/api/answer/route.ts
Comment thread src/lib/observability/error-capture.ts
Comment thread src/lib/rag.ts
@BigSimmo

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

@BigSimmo

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Cannot run autofix: This PR has merge conflicts.

Please resolve the conflicts with the base branch and try again.

Alternatively, use @coderabbitai resolve merge conflict to automatically resolve the conflicts.

auto-merge was automatically disabled July 13, 2026 15:26

Head branch was pushed to by a user without write access

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved — I merged main into this branch and fixed the package-lock.json conflict in commit f2002eb.

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved in f2002eb — this branch already includes the merge from main with conflicts fixed (including package-lock.json). I also rechecked locally and there are no remaining merge conflict markers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/observability/error-capture.ts`:
- Around line 44-46: Restrict the errorType value in the Sentry capture flow
around captureException to a fixed allowlist of known error names, falling back
to omitting the field or a safe constant for unrecognized names. Ensure custom
Error.name values cannot reach extra.errorType, and add a regression test using
a sensitive custom name to verify it is not sent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 703e87fa-c216-46e3-865d-e234209f65ad

📥 Commits

Reviewing files that changed from the base of the PR and between f2002eb and 79fa894.

📒 Files selected for processing (7)
  • src/app/api/answer/route.ts
  • src/app/api/answer/stream/route.ts
  • src/lib/env.ts
  • src/lib/observability/error-capture.ts
  • src/lib/rag.ts
  • tests/error-capture.test.ts
  • tests/instrumentation.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/lib/env.ts
  • tests/error-capture.test.ts
  • src/app/api/answer/stream/route.ts
  • src/lib/rag.ts
  • src/app/api/answer/route.ts
  • tests/instrumentation.test.ts

Comment thread src/lib/observability/error-capture.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85300fccc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app/api/answer/stream/route.ts Outdated
@github-actions

Copy link
Copy Markdown

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@BigSimmo
BigSimmo dismissed coderabbitai[bot]’s stale review July 13, 2026 16:38

All actionable findings were fixed, all review threads are resolved, focused privacy tests passed, and required CI is green.

@BigSimmo
BigSimmo merged commit 4ce001b into main Jul 13, 2026
18 checks passed
@BigSimmo
BigSimmo deleted the claude/sentry-server-capture branch July 14, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants